home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
028a
/
sq0928.zip
/
XSQ.C
< prev
next >
Wrap
C/C++ Source or Header
|
1990-09-13
|
27KB
|
1,032 lines
/*% cc -M2 -compat -i -K -O % -o sq
*
* xsq.c - CP/M compatible file squeezer utility
*
*/
static char *sccsid = "@(#)xsq.c 4.9 OMEN 09-12-90";
/*
* CHANGE HISTORY
*
* 4.9 Tweeks for compilation on VMS C
* 4.8 Preserves mod time of source file
* 4.7 Used stem() to shorten progress display
* 4.6 Eliminated "rescaling" message
* 4.5 Made likect static (bug fix)
* 4.4 Initialized keyptr for each file (bug fix).
* 4.3 Check for SQMAGIC in input file to detect already SQueezed file.
* Added checks for write errors (disk full, etc.).
* 4.2 Q is appended to filenames with two letter extensions rather
* than replacing second letter (file.11->file.11Q) to prevent some
* filename collisions (file.11 .vs. file.12).
* 4.1 Microsoft doesn't know to right shift, so use a mask instead.
* 4.0 Added Cipher key feature. % compression now considers header.
* 2.1u Deleted Greenlaw's address per his request. Works with C86 2.00
* but Lattice 2.0 results in bad files. Latest CC86 not tested.
* 2.0u Changes for VAX compatibility
* 2.0 Changes to allow operation with CC86 (CP/M-86) and
* Lattice C (MS-DOS).
* Lattice rewind() doesn't work
* and a special hack is needed for binary files.
* Added per cent compression display.
* Fixed bug that caused short output files.
* 1.9 small changes for slightly faster squeezing. About
* a third as fast as pack(1) (on the smae Unix machine).
* 1.7u machine independence was still lacking for 32-bit machines
* like the VAX-11/780, so a typedef was added. No action
* need be taken if running on a 16-bit machine, but if
* running on a VAX, define VAX either on the cc line or
* in the program preamble. Ben Goldfarb 12/13/82
* 1.6u machine independent output for file compatibility with
* original CP/M version SQ, when running on machine with
* IBM byte ordering such as Z8000 and 68000.
* 1.5u **nix version
* Filename generation changed to suit **nix and stdio.
*/
typedef short INT;
typedef unsigned short UNSIGNED;
#ifdef vax11c /* we only want 16 bit integers */
#include <types.h>
#include <stat.h>
#include <stdio.h>
#include <signal.h>
#else
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#endif
#ifndef BREAD
#define BREAD 0 /* Needed for some versions */
#endif
/* ****** include file signal.h for non Unix(TM) systems
#define SIG_IGN 1
#define SIG_DFL 0
#define signal(a,b) SIG_DFL
****************************************************** */
#define TRUE 1
#define FALSE 0
#define OK 0
#define ERROR (-1)
#define PATHLEN 312 /* Number of characters allowed in pathname */
#define ALTNAME "sq.out"
/* Definitions and external declarations */
#define SQMAGIC 0xFF76 /* Magic word indicates a SQueezed file */
#define KSQMAGIC 0xFF75 /* Cipher key magic word */
#define KEYSIZE 4096 /* Max size of key file */
/* *** Stuff for first translation module *** */
#define DLE 0x90
/* *** Stuff for second translation module *** */
#define SPEOF 256 /* special endfile token */
#define NUMVALS 257 /* 256 data values plus SPEOF*/
/* Definitions and external declarations */
int Verbose;
int Fullpathname;
int Key; /* flag for crypt key option */
unsigned char *Keyptr;
unsigned char *Keyend;
unsigned char Keybuf[KEYSIZE];
char Keyname[PATHLEN];
UNSIGNED crc; /* error check code */
FILE *Inbuff, *Outbuff; /* file buffers */
long Inbytes, Outbytes; /* counters for % compression calculation */
INT Numnodes; /* nbr of nodes in simplified tree */
/* *** Stuff for first translation module *** */
char state; /* states */
#define NOHIST 0 /* don't consider previous input*/
#define SENTCHAR 1 /* lastchar set, no lookahead yet */
#define SENDNEWC 2 /* newchar set, previous sequence done */
#define SENDCNT 3 /* newchar set, DLE sent, send count next */
/* *** Stuff for second translation module *** */
#define NOCHILD -1 /* indicates end of path through tree */
#define NUMNODES (NUMVALS + NUMVALS - 1) /* nbr of nodes */
#define MAXCOUNT ((UNSIGNED ) ~0) /* biggest unsigned short integer */
/*
* The following array of structures are the nodes of the
* binary trees. The first NUMVALS nodes become the leaves of the
* final tree and represent the values of the data bytes being
* encoded and the special endfile, SPEOF.
* The remaining nodes become the internal nodes of the final tree.
*/
struct nd {
UNSIGNED weight; /* number of appearances */
INT tdepth; /* length on longest path in tre*/
INT lchild, rchild; /* indices to next level */
} node[NUMNODES];
INT dctreehd; /* index to head node of final tree */
/*
* This is the encoding table:
* The bit strings have first bit in low bit.
* Note that counts were scaled so code fits unsigned short integer
*/
INT codelen[NUMVALS]; /* number of bits in code */
UNSIGNED code[NUMVALS]; /* code itself, right adjusted */
UNSIGNED tcode; /* temporary code value */
/* Variables used by encoding process */
INT curin; /* Value currently being encoded */
INT cbitsrem; /* Number of code string bits remaining */
UNSIGNED ccode; /* Current code shifted so next code bit is at right */
/* This program compresses a file without losing information.
* The usq.com program is required to unsqueeze the file
* before it can be used.
*
* Typical compression rates are:
* .EXE 12%
* .C 33%
* .DIC 46% (uppercase and a few others)
* .OUT 55% (nroff output)
* .PIC 80% (files with long runs)
*
* Calling the program without arguments gives usage info.
*
* The squeezed file name is formed by changing the second from last
* letter of the file type to Q. If there is no file type,
* the squeezed file type is Q. If the squeezed file name exists
* it is overwritten.
*
* The transformations compress strings of identical bytes and
* then encode each resulting byte value and EOF as bit strings
* having lengths in inverse proportion to their frequency of
* occurrence in the intermediate input stream. The latter uses
* the Huffman algorithm. Decoding information is included in
* the squeezed file, so squeezing small files or files with
* uniformly distributed byte values will actually increase size.
*/
/* special hack for Lattice C non-standard standard i/o */
int _fmode = 0x8000;
int Inforeground = 1;
INT gethuff(), getc_crc(), portgetw();
char *stem();
main(argc, argv)
char *argv[];
{
register i;
register char *cp;
register npats;
char **patts;
int n, errorstat;
FILE *kf;
if (signal(SIGINT, SIG_IGN)==SIG_IGN)
Inforeground = 0;
else
signal(SIGINT, SIG_DFL);
#ifdef SIGHUP
signal(SIGHUP, SIG_IGN);
#endif
npats=0; errorstat=0;
if (argc<2)
goto usage;
while (--argc) {
cp = *++argv;
if (*cp == '-') {
while( *++cp) {
switch(*cp) {
case 'f':
++Fullpathname; break;
case 'k':
if (--argc < 1)
goto usage;
strcpy(Keyname, *++argv);
if ( !(kf=fopen(Keyname, "r"))) {
perror(Keyname);
exit(0200);
}
i = fread(Keybuf, 1, KEYSIZE, kf);
fclose(kf);
if (i < 100) {
fprintf(stderr, "sq: Key file read error");
exit(0200);
}
fprintf(stderr, "sq: Keybuf %d bytes\n", i);
Keyend = i + Keybuf;
Key = TRUE;
break;
case 'v':
Verbose=TRUE; break;
default:
goto usage;
}
}
}
else if ( !npats && argc>0) {
if (argv[0][0]) {
npats=argc;
patts=argv;
}
}
}
if (npats < 1) {
usage:
fprintf(stderr,"File squeezer %s\n\tAlogrithim by Richard Greenlaw\n", sccsid+4);
fprintf(stderr, "Usage: sq [-k Keyfile] [-f] pathname ...\n");
fprintf(stderr, "\t-k Encrypt output with Keyfile\n");
fprintf(stderr, "\t-f Fullpathname\n");
exit(1);
}
for (n=0; n<npats; ++n)
errorstat |= obey(patts[n]);
exit(errorstat != 0);
}
obey(p)
char *p;
{
register char *q;
char outfile[PATHLEN+2]; /* output file spec. */
Keyptr = Keybuf;
/* Build output file name */
strcpy(outfile, stem(p));
for (q = outfile; *q; ++q) {
if (*q == '.') {
if (*++q == '\0')
*--q = '\0'; /* Kill trailing dot */
else {
switch (*++q) {
case '\0':
*q = 'Q';
*++q = '\0';
goto qnamed;
default:
if (q[1] == '\0') {
++q; q[1] = '\0';
}
*q = 'Q';
goto qnamed;
}
}
}
}
/* No file type */
strcat(outfile, ".Q");
qnamed:
if (strlen(outfile)>14)
return squeeze(p, ALTNAME);
else
return squeeze(p, outfile);
}
squeeze(infile, outfile)
char *infile, *outfile;
{
register INT c;
struct stat st;
time_t SaveTimes[2]; /* save access & mod times here (sg) */
if (Inforeground || Verbose)
fprintf(stderr, "%s -> %s: ", stem(infile), outfile);
if (stat(infile, &st)) {
perror(infile); return ERROR;
}
if ((st.st_mode & S_IFMT) != S_IFREG) {
fprintf(stderr, "sq: %s is not a regular file\n", infile);
return ERROR;
}
if ((Inbuff=fopen(infile, "rb")) == NULL) {
perror(infile);
return ERROR;
}
switch (portgetw(Inbuff)) { /* Check for header */
case KSQMAGIC:
case SQMAGIC:
if (Inforeground || Verbose)
fprintf(stderr, "already squeezed\n", infile);
else
fprintf(stderr,
"\r\nsq: %s is already squeezed\n", infile);
fclose(Inbuff); return OK;
default:
fseek(Inbuff, 0L, 0);
}
if ((Outbuff=fopen(outfile, "wb")) == NULL) {
perror(outfile);
fclose(Inbuff); return ERROR;
}
/* First pass - get properties of file */
crc = 0; /* initialize checksum */
if (Inforeground || Verbose)
fprintf(stderr, "analyzing, ");
init_ncr(); init_huff();
/* Write output file header with decoding info */
Outbytes = wrt_head(Outbuff, infile);
/* Second pass - encode the file */
clearerr(Inbuff); fseek(Inbuff, 0L, 0); Inbytes=0;
if (Inforeground || Verbose)
fprintf(stderr, "squeezing, ");
init_ncr(); /* For second pass */
/* Translate the input file into the output file */
while((c = gethuff()) != EOF) {
++Outbytes;
if (Key) {
c ^= *Keyptr;
if (++Keyptr >= Keyend)
Keyptr = Keybuf;
}
#ifdef DEBUG
printf("Transl: putc(%x)\n", c);
#endif
if (putc(c, Outbuff) == ERROR && ferror(Outbuff)) {
fprintf(stderr, "sq: write error\n");
exit(0200);
}
}
if (ferror(Inbuff)) {
fprintf(stderr, "sq: read error\n");
exit(0200);
}
if (Inforeground || Verbose) {
#ifdef DEBUG
fprintf(stderr, "%ld in %ld out ", Inbytes, Outbytes);
#endif
if (Inbytes)
fprintf(stderr, "%ld%% compression, %d Nodes.\n",
((Inbytes-Outbytes)*100L)/Inbytes, Numnodes);
else
fprintf(stderr, "Empty file.\n");
}
fclose(Inbuff);
fflush(Outbuff);
fclose(Outbuff);
#ifndef vax11c
/*
* Under 4.2BSD (Sun) st_atime & st_mtime aren't contiguous, so the
* date gets screwed up, but this change should work anywhere.... (sg)
*/
SaveTimes[0] = st.st_atime; /* time of last access */
SaveTimes[1] = st.st_mtime; /* time of last data modification */
utime(outfile, SaveTimes);
#endif
return OK;
}
/* First translation - encoding of repeated characters
* The code is byte for byte pass through except that
* DLE is encoded as DLE, zero and repeated byte values
* are encoded as value, DLE, count for count >= 3.
*/
init_ncr() /* initialize getcnr() */
{
state = NOHIST;
}
INT
getcnr()
{
static INT likect; /* count of consecutive identical chars */
static INT lastchar, newchar;
switch(state) {
case NOHIST:
/* No relevant history */
state = SENTCHAR;
return (lastchar = getc_crc());
case SENTCHAR:
/* Lastchar is set, need lookahead */
switch(lastchar) {
case DLE:
state = NOHIST;
return 0; /* indicates DLE was the data */
case EOF:
return EOF;
default:
for (likect=1; (newchar=getc_crc()) == lastchar && likect<255; ++likect)
;
switch(likect) {
case 1:
return (lastchar = newchar);
case 2:
/* just pass through */
state = SENDNEWC;
return lastchar;
default:
state = SENDCNT;
return DLE;
}
}
default:
fprintf(stderr,"sq: Internal Confusion - bad state\n");
exit(0200);
case SENDNEWC:
/* Previous sequence complete, newchar set */
state = SENTCHAR;
return (lastchar = newchar);
case SENDCNT:
/* Sent DLE for repeat sequence, send count */
state = SENDNEWC;
return likect;
}
}
/*
* Second translation - bytes to variable length bit strings
*
* This translation uses the Huffman algorithm to develop a
* binary tree representing the decoding information for
* a variable length bit string code for each input value.
* Each string's length is in inverse proportion to its
* frequency of appearance in the incoming data stream.
* The encoding table is derived from the decoding table.
*
* The range of valid values into the Huffman algorithm are
* the values of a byte stored in an integer plus the special
* endfile value chosen to be an adjacent value. Overall, 0-SPEOF.
*
* The "node" array of structures contains the nodes of the
* binary tree. The first NUMVALS nodes are the leaves of the
* tree and represent the values of the data bytes being
* encoded and the special endfile, SPEOF.
* The remaining nodes become the internal nodes of the tree.
*
* In the original design it was believed that
* a Huffman code would fit in the same number of
* bits that will hold the sum of all the counts.
* That was disproven by a user's file and was a rare but
* infamous bug. This version attempts to choose among equally
* weighted subtrees according to their maximum depths to avoid
* unnecessarily long codes. In case that is not sufficient
* to guarantee codes <= 16 bits long, we initially scale
* the counts so the total fits in an unsigned integer, but
* if codes longer than 16 bits are generated the counts are
* rescaled to a lower ceiling and code generation is retried.
*/
/* Initialize the Huffman translation. This requires reading
* the input file through any preceding translation functions
* to get the frequency distribution of the various values.
*/
init_huff()
{
register INT c;
register UNSIGNED *wp; /* simplifies weight counting */
register UNSIGNED ceiling; /* limit for scaling */
register INT i;
register INT listlen; /* length of btlist */
static INT btlist[NUMVALS]; /* list of intermediate binary trees */
/* Initialize tree nodes to no weight, no children */
init_tree();
/* Build frequency info in tree */
while ((c = getcnr()) != EOF)
if (*(wp = &node[c].weight) != MAXCOUNT)
++*wp;
++node[SPEOF].weight;
ceiling = MAXCOUNT;
do { /* Keep trying to scale and encode */
scale(ceiling);
ceiling /= 2; /* in case we rescale */
/* Build list of single node binary trees having
* leaves for the input values with non-zero counts
*/
for (i = listlen = 0; i < NUMVALS; ++i)
if (node[i].weight) {
node[i].tdepth = 0;
btlist[listlen++] = i;
}
/*
* Arrange list of trees into a heap with the entry
* indexing the node with the least weight at the top.
*/
#ifdef DEBUG
printf("calling heap: btlist= %d listlen= %d\n",
btlist, listlen);
#endif
heap(btlist, listlen);
/* Convert the list of trees to a single decoding tree */
bld_tree(btlist, listlen);
/* Initialize the encoding table */
init_enc();
/*
* Try to build encoding table.
* Fail if any code is > 16 bits long.
*/
}
while (buildenc(0, dctreehd));
/* Initialize encoding variables */
cbitsrem = 0; /* force initial read */
curin = 0; /* anything but endfile*/
}
/* The count of number of occurrances of each input value
* have already been prevented from exceeding MAXCOUNT.
* Now we must scale them so that their sum doesn't exceed
* ceiling and yet no non-zero count can become zero.
* This scaling prevents errors in the weights of the
* interior nodes of the Huffman tree and also ensures that
* the codes will fit in an unsigned integer. Rescaling is
* used if necessary to limit the code length.
*/
scale(ceil)
UNSIGNED ceil; /* upper limit on total weight */
{
register INT i;
register UNSIGNED w, sum;
register INT ovflw, divisor;
char increased; /* flag */
do {
for (i = sum = ovflw = 0; i < NUMVALS; ++i) {
if (node[i].weight > (ceil - sum))
++ovflw;
sum += node[i].weight;
}
divisor = ovflw + 1;
/* Ensure no non-zero values are lost */
increased = FALSE;
for (i = 0; i < NUMVALS; ++i) {
w = node[i].weight;
/* Don't fail to provide a code if it's used at all */
if (w < divisor && w != 0) {
node[i].weight = divisor;
increased = TRUE;
}
}
}
while(increased);
/* Scaling factor choosen, now scale */
if (divisor > 1)
for (i = 0; i < NUMVALS; ++i)
node[i].weight /= divisor;
}
/* heap() and adjust() maintain a list of binary trees as a
* heap with the top indexing the binary tree on the list
* which has the least weight or, in case of equal weights,
* least depth in its longest path. The depth part is not
* strictly necessary, but tends to avoid long codes which
* might provoke rescaling.
*/
heap(list, length)
INT list[];
INT length;
{
register INT i;
for (i = (length-2) / 2; i >= 0; --i)
adjust(list, i, length-1);
}
/* Make a heap from a heap with a new top */
adjust(list, top, bottom)
INT list[];
register INT top, bottom;
{
register INT k, temp;
k = 2 * top + 1; /* left child of top */
temp = list[top]; /* remember root node of top tree */
if ( k <= bottom) {
if ( k < bottom && cmptrees(list[k], list[k+1]))
++k;
/* k indexes "smaller" child (in heap of trees) of top */
/* now make top index "smaller" of old top and smallest child */
if (cmptrees(temp, list[k])) {
list[top] = list[k];
list[k] = temp;
/* Make the changed list a heap */
adjust(list, k, bottom); /* recursive*/
}
}
}
/*
* Compare two trees, if a > b return true, else return false
* note comparison rules in previous comments.
*/
cmptrees(a, b)
register INT a, b; /* root nodes of trees */
{
if (node[a].weight > node[b].weight)
return TRUE;
if (node[a].weight == node[b].weight)
if (node[a].tdepth > node[b].tdepth)
return TRUE;
return FALSE;
}
/* HUFFMAN ALGORITHM: develops the single element trees
* into a single binary tree by forming subtrees rooted in
* interior nodes having weights equal to the sum of weights of all
* their descendents and having depth counts indicating the
* depth of their longest paths.
*
* When all trees have been formed into a single tree satisfying
* the heap property (on weight, with depth as a tie breaker)
* then the binary code assigned to a leaf (value to be encoded)
* is then the series of left (0) and right (1)
* paths leading from the root to the leaf.
* Note that trees are removed from the heaped list by
* moving the last element over the top element and
* reheaping the smaller list.
*/
bld_tree(list, len)
INT list[];
register INT len;
{
register INT freenode; /* next free node in tree */
register struct nd *frnp; /* free node pointer */
register INT lch, rch; /* temps for left, right children */
/* Initialize index to next available (non-leaf) node.
* Lower numbered nodes correspond to leaves (data values).
*/
freenode = NUMVALS;
while(len > 1) {
/* Take from list two btrees with least weight
* and build an interior node pointing to them.
* This forms a new tree.
*/
lch = list[0]; /* This one will be left child */
/* delete top (least) tree from the list of trees */
list[0] = list[--len];
adjust(list, 0, len - 1);
/* Take new top (least) tree. Reuse list slot later */
rch = list[0]; /* This one will be right child */
/* Form new tree from the two least trees using
* a free node as root. Put the new tree in the list.
*/
frnp = &node[freenode]; /* address of next free node */
list[0] = freenode++; /* put at top for now */
frnp->lchild = lch;
frnp->rchild = rch;
frnp->weight = node[lch].weight + node[rch].weight;
frnp->tdepth = 1 + maxchar(node[lch].tdepth, node[rch].tdepth);
/* reheap list to get least tree at top*/
adjust(list, 0, len - 1);
}
dctreehd = list[0]; /* head of final tree */
}
/* ???????????? */
maxchar(a, b)
{
return ((a > b) ? a : b);
}
/* Initialize all nodes to single element binary trees
* with zero weight and depth.
*/
init_tree()
{
register INT i;
for (i = 0; i < NUMNODES; ++i) {
node[i].weight = 0;
node[i].tdepth = 0;
node[i].lchild = NOCHILD;
node[i].rchild = NOCHILD;
}
}
init_enc()
{
register INT i;
/* Initialize encoding table */
for (i = 0; i < NUMVALS; ++i) {
codelen[i] = 0;
}
}
/* Recursive routine to walk the indicated subtree and level
* and maintain the current path code in bstree. When a leaf
* is found the entire code string and length are put into
* the encoding table entry for the leaf's data value.
*
* Returns TRUE if codes are too long.
*/
unsigned mask[] = { 00,01,03,07,017,037,077,0177,0377,0777,01777,03777,07777,
017777,037777,077777,0177777,0177777,0177777,0177777,0177777,0177777,
0177777,0177777,0177777,0177777,0177777,0177777,0177777,
0177777,0177777,0177777,0177777,0177777,0177777,0177777,
0177777,0177777,0177777,0177777,0177777,0177777,0177777 };
buildenc(level, root)
INT level; /* level of tree being examined, from zero */
INT root; /* root of subtree is also data value if leaf */
{
register INT l, r;
l = node[root].lchild;
r = node[root].rchild;
#ifdef DEBUG
printf("buildenc: l= %d r= %d\n", l, r);
#endif
if ( l == NOCHILD && r == NOCHILD) {
/*
* Leaf. Previous path determines bit string
* code of length level (bits 0 to level - 1).
* Ensures unused code bits are zero.
*/
codelen[root] = level;
code[root] = tcode & mask[level];
#ifdef DEBUG
printf("buildenc: code[root]= %x tcode= %x level= %d\n",
code[root],tcode,level);
#endif
return (level > 16);
}
else {
if ( l != NOCHILD) {
/* Clear path bit and continue deeper */
tcode &= ~(1 << level);
#ifdef DEBUG
printf("tcode = %x ", tcode);
#endif
/* NOTE RECURSION */
if (buildenc(level + 1, l))
return TRUE;
}
if (r != NOCHILD) {
/* Set path bit and continue deeper */
tcode |= (1 << level);
/* NOTE RECURSION */
#ifdef DEBUG
printf("tcode = %x ", tcode);
#endif
if (buildenc(level + 1, r))
return TRUE;
}
}
return FALSE; /* if we got here we're ok so far */
}
/* Write out the header of the compressed file */
wrt_head(ob, infile)
FILE *ob;
char *infile; /* input file name (w/ or w/o drive) */
{
register INT l,r;
register INT i, k;
char *p; int nob;
putwe((Key?KSQMAGIC:SQMAGIC), ob); /* identifies as compressed */
putwe(crc, ob); /* unsigned sum of original data */
nob = 6; /* include Numnodes */
if (Key) { /* Record key file name w/o drive */
p = stem(Keyname);
do {
putc(*p, ob); ++nob;
}
while (*p++);
}
/* Record the original file name */
if (Fullpathname)
p = infile;
else
p = stem(infile);
do {
putc(*p, ob); ++nob;
}
while(*p++);
/* Write out a simplified decoding tree. Only the interior
* nodes are written. When a child is a leaf index
* (representing a data value) it is recoded as
* -(index + 1) to distinguish it from interior indexes
* which are recoded as positive indexes in the new tree.
* Note that this tree will be empty for an empty file.
*/
Numnodes = (dctreehd < NUMVALS) ? 0 : dctreehd - (NUMVALS -1);
putwe(Numnodes, ob);
for (k = 0, i = dctreehd; k < Numnodes; ++k, --i) {
l = node[i].lchild;
r = node[i].rchild;
l = (l < NUMVALS) ? -(l + 1) : dctreehd - l;
r = (r < NUMVALS) ? -(r + 1) : dctreehd - r;
putwe(l, ob); /* left child */
putwe(r, ob); /* right child */
nob += 4;
}
return nob;
}
/* Get an encoded byte or EOF. Reads from specified stream AS NEEDED.
*
* There are two unsynchronized bit-byte relationships here.
* The input stream bytes are converted to bit strings of
* various lengths via the static variables named c...
* These bit strings are concatenated without padding to
* become the stream of encoded result bytes, which this
* function returns one at a time. The EOF (end of file) is
* converted to SPEOF for convenience and encoded like any
* other input value. True EOF is returned after that.
*/
INT /* Returns byte values except for EOF */
gethuff()
{
register rbyte; /* Result byte value */
register need; /* Numbers of bits */
rbyte = 0;
need = 8; /* build one byte per call */
/*
* Loop to build a byte of encoded data
* Initialization forces read the first time
*/
loop:
if (cbitsrem >= need) {
/* Current code fullfills our needs */
#ifdef DEBUG
printf(" cbistrem=%d need=%d rbyte=%x\n",
cbitsrem, need, rbyte);
#endif
if (need == 0)
return (rbyte&0377);
/* Take what we need */
rbyte |= (ccode << (8 - need));
/* And leave the rest */
ccode >>= need;
cbitsrem -= need;
#ifdef DEBUG
printf(" returning need=%d rbyte=%x\n", need, rbyte);
#endif
return (rbyte&0377);
}
/* We need more than current code */
if (cbitsrem > 0) {
/* Take what there is */
rbyte |= (ccode << (8 - need));
need -= cbitsrem;
#ifdef DEBUG
printf(" need more=%d rbyte=%x\n", need, rbyte);
#endif
}
/* No more bits in current code string */
if (curin == SPEOF) {
/*
* The end of file token has been encoded. If
* result byte has data return it and do EOF next time
*/
cbitsrem = 0;
return (need == 8) ? EOF : (rbyte&0377);
}
/* Get an input byte */
if ((curin = getcnr()) == EOF)
curin = SPEOF; /* convenient for encoding */
/* Get the new byte's code */
ccode = code[curin];
cbitsrem = codelen[curin];
#ifdef DEBUG
printf("curin=%o ccode=%x cbitsrem=%d\n",
curin, ccode, cbitsrem);
#endif
goto loop;
}
/* Get next byte from file and update checksum */
INT
getc_crc()
{
register INT c;
c = getc(Inbuff);
if (c != EOF) {
++Inbytes;
crc += c; /* checksum */
}
return c;
}
/*
* Machine independent put-word that writes low order byte first
* (compatible with CP/M original) regardless of host CPU type.
*/
putwe(w, iob)
register int w;
register FILE *iob;
{
Outbytes += 2;
putc(w, iob);
putc(w>>8, iob);
if (ferror(iob)) {
fprintf(stderr, "sq: write error\n");
exit(0200);
}
}
/* Return pointer to file name part of pathname s */
char *
stem(s)
char *s;
{
register char *p;
for (p = s; *s; ) {
switch (*s++) {
case ':':
case '/':
case '\\':
p = s;
}
}
return p;
}
/*
* Machine independent getw which always gets bytes in the same order
* as the CP/M version of SQ wrote them
*/
INT
portgetw(f)
register FILE *f;
{
register short c;
c = getc(f) & 0377;
return (c | (getc(f) << 8));
}